package tips.p_1000.p251_300;

/**
 * 根据 百度百科 ，生命游戏，简称为生命，是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。
 * 给定一个包含 m × n 个格子的面板，每一个格子都可以看成是一个细胞。
 * 每个细胞都具有一个初始状态：1 即为活细胞（live），或 0 即为死细胞（dead）。
 * 每个细胞与其八个相邻位置（水平，垂直，对角线）的细胞都遵循以下四条生存定律：
 * <p>1、如果活细胞周围八个位置的活细胞数少于两个，则该位置活细胞死亡；
 * <p>2、如果活细胞周围八个位置有两个或三个活细胞，则该位置活细胞仍然存活；
 * <p>3、如果活细胞周围八个位置有超过三个活细胞，则该位置活细胞死亡；
 * <p>4、如果死细胞周围正好有三个活细胞，则该位置死细胞复活；
 * 下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的，其中细胞的出生和死亡是同时发生的。给你 m x n 网格面板 board 的当前状态，返回下一个状态。
 * <p>示例 1：
 * 输入：board = [[0,1,0],[0,0,1],[1,1,1],[0,0,0]]
 * 输出：[[0,0,0],[1,0,1],[0,1,1],[0,1,0]]
 * <p>示例 2：
 * 输入：board = [[1,1],[1,0]]
 * 输出：[[1,1],[1,1]]
 * <p>提示：
 * m == board.length
 * n == board[i].length
 * 1 <= m, n <= 25
 * board[i][j] 为 0 或 1
 * <p>进阶：
 * 你可以使用原地算法解决本题吗？请注意，面板上所有格子需要同时被更新：你不能先更新某些格子，然后使用它们的更新后的值再更新其他格子。
 * 本题中，我们使用二维数组来表示面板。原则上，面板是无限的，但当活细胞侵占了面板边界时会造成问题。你将如何解决这些问题？
 *
 * @author hc
 */
public class Demo289 {

    private static final int[] DX = {0, 0, 1, -1, 1, 1, -1, -1};
    private static final int[] DY = {1, -1, 0, 0, 1, -1, 1, -1};

    public void gameOfLife(int[][] board) {
        if (board.length == 0) {
            return;
        }
        int m = board.length;
        int n = board[0].length;
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                // 记录周围活细胞个数
                int cnt = 0;
                for (int k = 0; k < 8; k++) {
                    int x = i + DX[k];
                    int y = j + DY[k];
                    if (x < 0 || x >= board.length || y < 0 || y >= board[0].length) {
                        continue;
                    }
                    // board[x][y] & 1 利用 int 最后一位记录当前值 0/1
                    cnt += board[x][y] & 1;
                }

                if ((board[i][j] & 1) > 0) {
                    // 当前为活细胞 0b01
                    if (cnt >= 2 && cnt <= 3) {
                        // 规则二 原数据 0b01 -> 0b11
                        board[i][j] = 0b11;
                    }
                    // 周围活细胞过多或过少都会死，原数据 0b01 -> 0b01
                } else if (cnt == 3) {
                    // 当前为死细胞 0b00 -> 0b10
                    board[i][j] = 0b10;
                }
            }
        }
        // int 倒数第二位记录更新后状态 右移状态切换
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                board[i][j] >>= 1;
            }
        }
    }
}
